package com.record.service.impl;



import com.record.dto.AnalysisResultDto;
import com.record.dto.ItemCountDto;
import com.record.service.EccService;
import com.record.utils.*;
import org.apache.commons.math3.distribution.ChiSquaredDistribution;
import org.apache.commons.math3.ml.clustering.CentroidCluster;
import org.apache.commons.math3.ml.clustering.DoublePoint;
import org.apache.commons.math3.ml.clustering.KMeansPlusPlusClusterer;
import org.apache.commons.math3.ml.distance.EuclideanDistance;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.apache.poi.ss.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.math3.distribution.NormalDistribution;

import static com.record.utils.ClusterUtils.estimateBestK;


@Service
public class EccServiceImpl implements EccService {


    @Override
    public AnalysisResultDto analyzeExcel(MultipartFile file) throws IOException {
        Map<String, Long> counter = new HashMap<>();
        long total = 0L;

        try (InputStream is = file.getInputStream();
             Workbook wb = WorkbookFactory.create(is)) {

            Sheet sheet = wb.getSheetAt(0);
            Row header = sheet.getRow(0);
            if (header == null || !"统计分析".equals(header.getCell(0).toString().trim())) {
                throw new IllegalArgumentException("第一行A1必须为‘统计分析’");
            }

            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;
                Cell cell = row.getCell(0);
                if (cell == null) continue;

                String val = cell.toString().trim();
                if (val.isEmpty()) continue;

                counter.merge(val, 1L, Long::sum); // ✅ 正确写法
                total++;
            }
        }

        List<ItemCountDto> items = counter.entrySet()
                .stream()
                .map(e -> new ItemCountDto(e.getKey(), e.getValue()))
                .collect(Collectors.toList());

        // …后续统计逻辑
        return new AnalysisResultDto(total, items);
    }



    //求解平均值
    @Override
    public Map<String, Object> analyzeGroupAverage(MultipartFile file) throws IOException {
        Map<String, Object> result = new HashMap<>();

        try (InputStream is = file.getInputStream(); Workbook wb = WorkbookFactory.create(is)) {
            Sheet sheet = wb.getSheetAt(0);
            Row header = sheet.getRow(0);

            if (header == null) {
                throw new IllegalArgumentException("Excel 首行不能为空，必须包含列标题。");
            }

            int columnCount = header.getLastCellNum();

            // 超过三列直接返回提示
            if (columnCount > 3) {
                result.put("message", "您已超过三组，系统不计算。");
                result.put("status", "error");
                return result;
            }

            // 遍历每一列计算平均值
            for (int c = 0; c < columnCount; c++) {
                Cell headerCell = header.getCell(c);
                if (headerCell == null) continue;
                String title = headerCell.toString().trim();
                if (title.isEmpty()) title = "第" + (c + 1) + "列";

                double sum = 0.0;
                int count = 0;

                for (int r = 1; r <= sheet.getLastRowNum(); r++) {
                    Row row = sheet.getRow(r);
                    if (row == null) continue;
                    Cell cell = row.getCell(c);
                    if (cell == null || cell.getCellType() == CellType.BLANK) continue;

                    try {
                        double value = cell.getNumericCellValue();
                        sum += value;
                        count++;
                    } catch (Exception e) {
                        // 不是数字的单元格跳过
                    }
                }

                double avg = count > 0 ? sum / count : 0.0;
                result.put(title, avg);
            }

            result.put("status", "success");
        }

        return result;
    }


    /**
     * 标准差
     * @param file
     * @return
     * @throws IOException
     */
    @Override
    public Map<String, Object> analyzeGroupStd(MultipartFile file) throws IOException {
        Map<String, Object> result = new HashMap<>();

        try (InputStream is = file.getInputStream(); Workbook wb = WorkbookFactory.create(is)) {
            Sheet sheet = wb.getSheetAt(0);
            Row header = sheet.getRow(0);

            if (header == null) {
                throw new IllegalArgumentException("Excel 首行不能为空，必须包含列标题。");
            }

            int columnCount = header.getLastCellNum();

            // 超过三列直接返回提示
            if (columnCount > 3) {
                result.put("message", "您已超过三组，系统不计算。");
                result.put("status", "error");
                return result;
            }

            // 遍历每一列计算标准差
            for (int c = 0; c < columnCount; c++) {
                Cell headerCell = header.getCell(c);
                if (headerCell == null) continue;
                String title = headerCell.toString().trim();
                if (title.isEmpty()) title = "第" + (c + 1) + "列";

                List<Double> values = new ArrayList<>();

                for (int r = 1; r <= sheet.getLastRowNum(); r++) {
                    Row row = sheet.getRow(r);
                    if (row == null) continue;
                    Cell cell = row.getCell(c);
                    if (cell == null || cell.getCellType() == CellType.BLANK) continue;

                    try {
                        values.add(cell.getNumericCellValue());
                    } catch (Exception e) {
                        // 非数值类型跳过
                    }
                }

                if (values.isEmpty()) {
                    result.put(title, 0.0);
                    continue;
                }

                // 计算均值
                double mean = values.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);

                // 计算方差
                double variance = values.stream()
                        .mapToDouble(v -> Math.pow(v - mean, 2))
                        .average()
                        .orElse(0.0);

                // 计算标准差
                double stdDev = Math.sqrt(variance);

                result.put(title, stdDev);
            }

            result.put("status", "success");
        }

        return result;
    }


    /**
     * 正态分布-计算W和P
     * @param file
     * @return
     * @throws IOException
     */
    @Override
    public Map<String, Object> analyzeShapiro(MultipartFile file) throws IOException {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream inputStream = file.getInputStream()) {
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);

            Row headerRow = sheet.getRow(0);
            if (headerRow == null) throw new IllegalArgumentException("Excel 首行不能为空");
            int columnCount = headerRow.getPhysicalNumberOfCells();

            for (int col = 0; col < columnCount; col++) {
                Cell headerCell = headerRow.getCell(col);
                if (headerCell == null) continue;

                String title = headerCell.toString().trim();
                if (title.isEmpty()) title = "第" + (col + 1) + "列";

                List<Double> values = new ArrayList<>();

                for (int row = 1; row <= sheet.getLastRowNum(); row++) {
                    Row currentRow = sheet.getRow(row);
                    if (currentRow == null) continue;
                    Cell cell = currentRow.getCell(col);
                    if (cell == null) continue;

                    switch (cell.getCellType()) {
                        case NUMERIC:
                            values.add(cell.getNumericCellValue());
                            break;
                        case STRING:
                            try { values.add(Double.parseDouble(cell.getStringCellValue())); } catch (Exception ignore) {}
                            break;
                    }
                }

                if (values.isEmpty()) continue;
                double[] data = values.stream().mapToDouble(Double::doubleValue).toArray();

                ShapiroWilkTest test = new ShapiroWilkTest(data);
                Map<String, Object> colResult = new LinkedHashMap<>();
                colResult.put("W", test.getW());
                colResult.put("p", test.getPValue());
                colResult.put("isNormal", test.getPValue() > 0.05 ? "是" : "否");

                result.put(title, colResult);
            }

            result.put("status", "success");
        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }

    @Override
    public Map<String, Object> analyzeVariance(MultipartFile file) throws IOException {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream is = file.getInputStream()) {
            Workbook wb = WorkbookFactory.create(is);
            Sheet sheet = wb.getSheetAt(0);
            Row header = sheet.getRow(0);

            if (header == null) {
                throw new IllegalArgumentException("Excel 首行不能为空，必须包含列标题。");
            }

            int columnCount = header.getPhysicalNumberOfCells();
            if (columnCount > 3) {
                result.put("status", "error");
                result.put("message", "最多只允许上传三列数据进行方差齐性检验。");
                return result;
            }

            // 读取每列数据
            List<double[]> groups = new ArrayList<>();
            for (int c = 0; c < columnCount; c++) {
                List<Double> values = new ArrayList<>();

                for (int r = 1; r <= sheet.getLastRowNum(); r++) {
                    Row row = sheet.getRow(r);
                    if (row == null) continue;
                    Cell cell = row.getCell(c);
                    if (cell == null) continue;

                    if (cell.getCellType() == CellType.NUMERIC) {
                        values.add(cell.getNumericCellValue());
                    } else if (cell.getCellType() == CellType.STRING) {
                        try {
                            values.add(Double.parseDouble(cell.getStringCellValue()));
                        } catch (Exception ignore) {}
                    }
                }

                if (!values.isEmpty()) {
                    groups.add(values.stream().mapToDouble(Double::doubleValue).toArray());
                }
            }

            if (groups.size() < 2) {
                throw new IllegalArgumentException("至少需要两组数据进行方差齐性检验。");
            }

            // 调用 Levene 检验
            LeveneTest test = new LeveneTest(groups);

            result.put("F", test.getF());
            result.put("P", test.getPValue());
            result.put("isHomogeneous", test.getPValue() > 0.05 ? "是" : "否");
            result.put("status", "success");

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }

    @Override
    public Map<String, Object> analyzeAnova(MultipartFile file) throws IOException {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream is = file.getInputStream()) {
            Workbook workbook = WorkbookFactory.create(is);
            Sheet sheet = workbook.getSheetAt(0);
            Row header = sheet.getRow(0);

            if (header == null) {
                throw new IllegalArgumentException("Excel 首行不能为空");
            }

            int columnCount = header.getPhysicalNumberOfCells();
            if (columnCount < 2) {
                throw new IllegalArgumentException("至少需要两列数据进行方差分析");
            }
            if (columnCount > 15) {
                result.put("status", "error");
                result.put("message", "最多允许15列数据进行方差分析");
                return result;
            }

            // 读取每列数据
            List<double[]> groups = new ArrayList<>();
            Map<String, double[]> groupMap = new LinkedHashMap<>();

            for (int c = 0; c < columnCount; c++) {
                String title = header.getCell(c).getStringCellValue();
                List<Double> values = new ArrayList<>();

                for (int r = 1; r <= sheet.getLastRowNum(); r++) {
                    Row row = sheet.getRow(r);
                    if (row == null) continue;
                    Cell cell = row.getCell(c);
                    if (cell == null) continue;

                    if (cell.getCellType() == CellType.NUMERIC) {
                        values.add(cell.getNumericCellValue());
                    } else if (cell.getCellType() == CellType.STRING) {
                        try {
                            values.add(Double.parseDouble(cell.getStringCellValue()));
                        } catch (Exception ignore) {}
                    }
                }

                if (!values.isEmpty()) {
                    double[] arr = values.stream().mapToDouble(Double::doubleValue).toArray();
                    groups.add(arr);
                    groupMap.put(title, arr);
                }
            }

            if (groups.size() < 2) {
                throw new IllegalArgumentException("有效数据列不足，无法进行方差分析");
            }

            // 执行单因素方差分析
            AnovaResult anova = AnovaCalculator.oneWayAnova(groups);

            // 计算每列的均值与标准差（便于展示）
            Map<String, Object> means = new LinkedHashMap<>();
            Map<String, Object> stds = new LinkedHashMap<>();
            for (Map.Entry<String, double[]> entry : groupMap.entrySet()) {
                double[] data = entry.getValue();
                double mean = Arrays.stream(data).average().orElse(0.0);
                double std = Math.sqrt(Arrays.stream(data)
                        .map(v -> Math.pow(v - mean, 2)).sum() / (data.length - 1));
                means.put(entry.getKey(), mean);
                stds.put(entry.getKey(), std);
            }

            // 结果封装
            result.put("F", anova.getF());
            result.put("P", anova.getPValue());
            result.put("isSignificant", anova.getPValue() < 0.05 ? "是" : "否");
            result.put("means", means);
            result.put("stds", stds);
            result.put("status", "success");

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }

    @Override
    public Map<String, Object> analyzeTTest(MultipartFile file) throws IOException {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream is = file.getInputStream()) {
            Workbook workbook = WorkbookFactory.create(is);
            Sheet sheet = workbook.getSheetAt(0);
            Row header = sheet.getRow(0);

            if (header == null) {
                throw new IllegalArgumentException("Excel 首行不能为空");
            }

            int columnCount = header.getPhysicalNumberOfCells();
            if (columnCount < 2) {
                throw new IllegalArgumentException("至少需要两列数据进行t检验");
            }
            if (columnCount > 3) {
                result.put("status", "error");
                result.put("message", "最多支持3列数据进行t检验");
                return result;
            }

            // 读取每列数据
            Map<String, double[]> groups = new LinkedHashMap<>();
            for (int c = 0; c < columnCount; c++) {
                String title = header.getCell(c).getStringCellValue();
                List<Double> values = new ArrayList<>();
                for (int r = 1; r <= sheet.getLastRowNum(); r++) {
                    Row row = sheet.getRow(r);
                    if (row == null) continue;
                    Cell cell = row.getCell(c);
                    if (cell == null) continue;

                    if (cell.getCellType() == CellType.NUMERIC) {
                        values.add(cell.getNumericCellValue());
                    } else if (cell.getCellType() == CellType.STRING) {
                        try {
                            values.add(Double.parseDouble(cell.getStringCellValue()));
                        } catch (Exception ignore) {}
                    }
                }

                if (!values.isEmpty()) {
                    groups.put(title, values.stream().mapToDouble(Double::doubleValue).toArray());
                }
            }

            if (groups.size() < 2) {
                throw new IllegalArgumentException("有效列不足2列，无法进行t检验");
            }

            List<String> titles = new ArrayList<>(groups.keySet());
            Map<String, Object> comparisons = new LinkedHashMap<>();

            // 两两配对
            for (int i = 0; i < titles.size(); i++) {
                for (int j = i + 1; j < titles.size(); j++) {
                    String key = titles.get(i) + " vs " + titles.get(j);
                    double[] g1 = groups.get(titles.get(i));
                    double[] g2 = groups.get(titles.get(j));

                    TTestResult tResult = TTestCalculator.independentTTest(g1, g2);

                    Map<String, Object> pairResult = new LinkedHashMap<>();
                    pairResult.put("t", tResult.getT());
                    pairResult.put("P", tResult.getPValue());
                    pairResult.put("df", tResult.getDf());
                    pairResult.put("isSignificant", tResult.getPValue() < 0.05 ? "是" : "否");

                    comparisons.put(key, pairResult);
                }
            }

            result.put("comparisons", comparisons);
            result.put("status", "success");

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }


    /**
     * 非参检验
     * @param file
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> analyzeNonParametricFull(MultipartFile file) throws IOException {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream inputStream = file.getInputStream()) {
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet == null) throw new IllegalArgumentException("Excel 工作表不能为空");

            // 读取表头
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) throw new IllegalArgumentException("首行不能为空");

            int columnCount = headerRow.getPhysicalNumberOfCells();
            if (columnCount < 2) throw new IllegalArgumentException("至少需要两列数据");
            if (columnCount > 3) throw new IllegalArgumentException("当前仅支持最多三列");

            // === Step 1. 读取每列数据 ===
            Map<String, double[]> groups = new LinkedHashMap<>();
            for (int col = 0; col < columnCount; col++) {
                String title = headerRow.getCell(col).getStringCellValue().trim();
                List<Double> values = new ArrayList<>();

                for (int rowIdx = 1; rowIdx <= sheet.getLastRowNum(); rowIdx++) {
                    Row row = sheet.getRow(rowIdx);
                    if (row == null) continue;
                    Cell cell = row.getCell(col);
                    if (cell == null) continue;

                    if (cell.getCellType() == CellType.NUMERIC) {
                        values.add(cell.getNumericCellValue());
                    } else if (cell.getCellType() == CellType.STRING) {
                        try {
                            values.add(Double.parseDouble(cell.getStringCellValue()));
                        } catch (Exception ignored) {}
                    }
                }

                if (!values.isEmpty()) {
                    groups.put(title, values.stream().mapToDouble(Double::doubleValue).toArray());
                }
            }

            if (groups.size() < 2) {
                throw new IllegalArgumentException("有效列数据不足 2 组");
            }

            // === Step 2. Mann–Whitney 两两检验 ===
            List<String> titles = new ArrayList<>(groups.keySet());
            Map<String, Object> comparisons = new LinkedHashMap<>();

            for (int i = 0; i < titles.size(); i++) {
                for (int j = i + 1; j < titles.size(); j++) {
                    String key = titles.get(i) + " vs " + titles.get(j);
                    double[] g1 = groups.get(titles.get(i));
                    double[] g2 = groups.get(titles.get(j));

                    MannWhitneyResult test = MannWhitneyUCalculator.test(g1, g2);

                    Map<String, Object> pairResult = new LinkedHashMap<>();
                    pairResult.put("U", round(test.getU()));
                    pairResult.put("Z", round(test.getZ()));
                    pairResult.put("P", round(test.getPValue()));
                    pairResult.put("isSignificant", test.getPValue() < 0.05 ? "是" : "否");

                    comparisons.put(key, pairResult);
                }
            }

            // === Step 3. Kruskal–Wallis 三组检验 ===
            if (groups.size() == 3) {
                double[][] data = groups.values().toArray(new double[0][]);
                KruskalWallisResult kw = KruskalWallisCalculator.test(data);

                Map<String, Object> kwResult = new LinkedHashMap<>();
                kwResult.put("H", round(kw.getH()));
                kwResult.put("P", round(kw.getPValue()));
                kwResult.put("isSignificant", kw.getPValue() < 0.05 ? "是" : "否");

                comparisons.put("1 vs 2 vs 3（Kruskal–Wallis H检验）", kwResult);
            }

            result.put("comparisons", comparisons);
            result.put("status", "success");

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }




    /** 小数保留（四舍五入）（这是调用的一个方法） */
    private double round(double value) {
        return Math.round(value * 1e6) / 1e6;
    }


    /**
     *
     * @param file
     * @return
     */
    @Override
    public Map<String, Object> analyzeLinearRegression(MultipartFile file)throws IOException {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream inputStream = file.getInputStream()) {
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet == null) throw new IllegalArgumentException("Excel 工作表不能为空");

            Row headerRow = sheet.getRow(0);
            if (headerRow == null || headerRow.getPhysicalNumberOfCells() < 2)
                throw new IllegalArgumentException("Excel 至少需要两列（x 和 y）");

            // 读取标题
            String xName = headerRow.getCell(0).getStringCellValue().trim();
            String yName = headerRow.getCell(1).getStringCellValue().trim();

            List<Double> xList = new ArrayList<>();
            List<Double> yList = new ArrayList<>();

            // 从第二行开始读取数据
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;

                Cell xCell = row.getCell(0);
                Cell yCell = row.getCell(1);
                if (xCell == null || yCell == null) continue;

                if (xCell.getCellType() == CellType.NUMERIC && yCell.getCellType() == CellType.NUMERIC) {
                    xList.add(xCell.getNumericCellValue());
                    yList.add(yCell.getNumericCellValue());
                }
            }

            if (xList.size() < 3)
                throw new IllegalArgumentException("样本量太少（至少3组数据）");

            double[] x = xList.stream().mapToDouble(Double::doubleValue).toArray();
            double[] y = yList.stream().mapToDouble(Double::doubleValue).toArray();

            // 创建回归模型
            SimpleRegression regression = new SimpleRegression();
            for (int i = 0; i < x.length; i++) {
                regression.addData(x[i], y[i]);
            }

            // 计算指标
            double intercept = regression.getIntercept();
            double slope = regression.getSlope();
            double r2 = regression.getRSquare();
            double pValue = regression.getSignificance();

            Map<String, Object> analysis = new LinkedHashMap<>();
            analysis.put("截距 (β₀)", round(intercept));
            analysis.put("斜率 (β₁)", round(slope));
            analysis.put("决定系数 (R²)", round(r2));
            analysis.put("P", round(pValue));
            analysis.put("显著性", pValue < 0.05 ? "是" : "否");
            analysis.put("回归方程", String.format("%s = %.4f + %.4f × %s", yName, intercept, slope, xName));

            result.put("线性回归结果", analysis);
            result.put("status", "success");

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", e.getMessage());
        }

        return result;
    }

    /**
     * 聚类分析
     * @param file
     * @param k
     * @return
     */
    @Override
    public Map<String, Object> clusterAnalysis(MultipartFile file, Integer k) {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream inputStream = file.getInputStream()) {

            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet == null) throw new IllegalArgumentException("Excel 工作表不能为空");

            Row header = sheet.getRow(0);
            if (header == null) throw new IllegalArgumentException("Excel 必须包含表头");

            int colCount = header.getPhysicalNumberOfCells();
            int rowCount = sheet.getLastRowNum();

            if (colCount < 2 || rowCount < 1)
                throw new IllegalArgumentException("数据不足，至少需要两列（含ID列）和一行数据");

            // 读取列名（特征名称）
            List<String> colNames = new ArrayList<>();
            for (int j = 1; j < colCount; j++) {  // 从第二列开始（第1列是ID）
                Cell cell = header.getCell(j);
                colNames.add(cell != null ? cell.getStringCellValue().trim() : "特征" + j);
            }

            // 每一行作为一个样本
            List<DoublePoint> points = new ArrayList<>();
            List<String> ids = new ArrayList<>();

            for (int i = 1; i <= rowCount; i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;

                // 检查是否为有效行（至少有一个非空数字）
                boolean hasData = false;
                for (int j = 1; j < colCount; j++) {
                    Cell cell = row.getCell(j);
                    if (cell != null && cell.getCellType() == CellType.NUMERIC) {
                        hasData = true;
                        break;
                    }
                }
                if (!hasData) continue;

                // 读取ID（第一列）
                Cell idCell = row.getCell(0);
                String id = "样本" + i;
                if (idCell != null) {
                    if (idCell.getCellType() == CellType.STRING) {
                        id = idCell.getStringCellValue().trim();
                    } else if (idCell.getCellType() == CellType.NUMERIC) {
                        id = "样本" + (int) idCell.getNumericCellValue();
                    }
                }
                ids.add(id);

                // 读取特征列（从第2列开始）
                List<Double> features = new ArrayList<>();
                for (int j = 1; j < colCount; j++) {
                    Cell cell = row.getCell(j);
                    double value = 0.0;
                    if (cell != null) {
                        if (cell.getCellType() == CellType.NUMERIC) {
                            value = cell.getNumericCellValue();
                        } else if (cell.getCellType() == CellType.STRING) {
                            try {
                                value = Double.parseDouble(cell.getStringCellValue());
                            } catch (NumberFormatException ignored) {
                                value = 0.0;
                            }
                        }
                    }
                    features.add(value);
                }

                double[] featureArray = features.stream().mapToDouble(Double::doubleValue).toArray();
                points.add(new DoublePoint(featureArray));
            }

            // 去除重复ID样本
            Set<String> seen = new HashSet<>();
            List<String> uniqueIds = new ArrayList<>();
            List<DoublePoint> uniquePoints = new ArrayList<>();
            for (int i = 0; i < ids.size(); i++) {
                if (seen.add(ids.get(i))) {
                    uniqueIds.add(ids.get(i));
                    uniquePoints.add(points.get(i));
                }
            }
            ids = uniqueIds;
            points = uniquePoints;

            if (points.size() < 2)
                throw new IllegalArgumentException("样本量不足，无法进行聚类分析");

            // 自动估计K值（使用肘部法）
            if (k == null) {
                k = ClusterUtils.estimateBestK(points);
            }
            k = Math.min(k, points.size());

            // 执行KMeans聚类
            KMeansPlusPlusClusterer<DoublePoint> clusterer =
                    new KMeansPlusPlusClusterer<>(k, 100, new EuclideanDistance());
            List<CentroidCluster<DoublePoint>> clusters = clusterer.cluster(points);

            // 概要信息
            Map<String, Object> summary = new LinkedHashMap<>();
            summary.put("样本总数", points.size());
            summary.put("特征维度", colCount - 1);
            summary.put("聚类数", k);

            // 聚类结果
            List<Map<String, Object>> clusterList = new ArrayList<>();
            int clusterIndex = 1;

            for (CentroidCluster<DoublePoint> cluster : clusters) {
                List<Integer> indices = cluster.getPoints().stream()
                        .map(points::indexOf)
                        .collect(Collectors.toList());

                List<String> memberIds = indices.stream().map(ids::get).collect(Collectors.toList());
                double[] center = cluster.getCenter().getPoint();

                Map<String, Object> clusterInfo = new LinkedHashMap<>();
                clusterInfo.put("类别编号", "Cluster-" + clusterIndex++);
                clusterInfo.put("成员ID", memberIds);
                clusterInfo.put("成员数", memberIds.size());
                clusterInfo.put("特征名称", colNames);  // ✅ 新增特征名输出
                clusterInfo.put("聚类中心（按列顺序）", Arrays.stream(center)
                        .mapToObj(v -> String.format("%.2f", v))
                        .collect(Collectors.toList()));

                clusterList.add(clusterInfo);
            }

            result.put("概要信息", summary);
            result.put("聚类结果", clusterList);
            result.put("status", "success");

        } catch (Exception e) {
            e.printStackTrace();
            result.put("status", "error");
            result.put("message", e.toString());
        }

        return result;
    }



    /**
     *
     * @param file
     * @param sheetIndex
     * @return
     */
    @Override
    public Map<String, Object> chiSquareTest(MultipartFile file, Integer sheetIndex) {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream inputStream = file.getInputStream()) {
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(sheetIndex != null ? sheetIndex : 0);

            // 读取矩阵数据（跳过第1行第1列）
            List<double[]> matrixList = new ArrayList<>();
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;

                List<Double> rowValues = new ArrayList<>();
                for (int j = 1; j < row.getLastCellNum(); j++) {
                    Cell cell = row.getCell(j);
                    if (cell == null) continue;
                    if (cell.getCellType() == CellType.NUMERIC) {
                        rowValues.add(cell.getNumericCellValue());
                    } else {
                        break; // 到“总计”列提前终止
                    }
                }
                if (!rowValues.isEmpty()) matrixList.add(rowValues.stream().mapToDouble(Double::doubleValue).toArray());
            }

            // 去掉“总计”行
            if (matrixList.size() > 1) matrixList.remove(matrixList.size() - 1);

            // 转为二维数组
            double[][] observed = matrixList.toArray(new double[0][]);
            int rows = observed.length;
            int cols = observed[0].length;

            // 去掉“总计”列（最后一列）
            if (cols > 1) {
                double[][] tmp = new double[rows][cols - 1];
                for (int i = 0; i < rows; i++) {
                    tmp[i] = Arrays.copyOf(observed[i], cols - 1);
                }
                observed = tmp;
                cols -= 1;
            }

            // 校验最少2x2
            if (rows < 2 || cols < 2) {
                result.put("status", "error");
                result.put("message", "表格数据不足，至少需要2×2列联表。");
                return result;
            }

            // 总和
            double total = 0;
            for (double[] r : observed) for (double v : r) total += v;

            // 行和、列和
            double[] rowSums = new double[rows];
            double[] colSums = new double[cols];
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    rowSums[i] += observed[i][j];
                    colSums[j] += observed[i][j];
                }
            }

            // 期望值矩阵
            double[][] expected = new double[rows][cols];
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    expected[i][j] = rowSums[i] * colSums[j] / total;
                }
            }

            // 卡方统计量
            double chiSquare = 0.0;
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    chiSquare += Math.pow(observed[i][j] - expected[i][j], 2) / expected[i][j];
                }
            }

            // 自由度与P值
            int df = (rows - 1) * (cols - 1);
            ChiSquaredDistribution dist = new ChiSquaredDistribution(df);
            double pValue = 1 - dist.cumulativeProbability(chiSquare);

            // 输出结果
            result.put("status", "success");
            result.put("rows", rows);
            result.put("cols", cols);
            result.put("total", total);
            result.put("chiSquare", String.format("%.4f", chiSquare));
            result.put("df", df);
            result.put("pValue", String.format("%.5f", pValue));
            result.put("isSignificant", pValue < 0.05 ? "是（p < 0.05）" : "否（p ≥ 0.05）");
            result.put("interpretation",
                    pValue < 0.05 ?
                            "组别与结果存在显著关联，差异具有统计学意义。" :
                            "组别与结果无显著差异。");

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", "计算出错：" + e.getMessage());
        }

        return result;
    }

    @Override
    public Map<String, Object> analyzeAssociationRules(MultipartFile file, Integer sheetIndex) {
        Map<String, Object> result = new LinkedHashMap<>();

        try (InputStream inputStream = file.getInputStream()) {
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(sheetIndex != null ? sheetIndex : 0);

            // 读取表头（项目名称）
            Row header = sheet.getRow(0);
            if (header == null) throw new IllegalArgumentException("Excel表格必须包含表头！");
            int colCount = header.getPhysicalNumberOfCells();
            List<String> itemNames = new ArrayList<>();
            for (int j = 1; j < colCount; j++) { // 从第二列开始
                itemNames.add(header.getCell(j).getStringCellValue());
            }

            // 读取数据
            List<int[]> transactions = new ArrayList<>();
            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) continue;
                int[] record = new int[colCount - 1];
                for (int j = 1; j < colCount; j++) {
                    Cell cell = row.getCell(j);
                    record[j - 1] = (cell != null && cell.getCellType() == CellType.NUMERIC && cell.getNumericCellValue() > 0.5) ? 1 : 0;
                }
                transactions.add(record);
            }

            int n = transactions.size(); // 样本数
            if (n == 0) throw new IllegalArgumentException("数据为空！");

            List<Map<String, Object>> rules = new ArrayList<>();

            // 计算两两组合的支持度、置信度、提升度
            for (int i = 0; i < itemNames.size(); i++) {
                for (int j = 0; j < itemNames.size(); j++) {
                    if (i == j) continue;

                    int countA = 0, countB = 0, countAB = 0;
                    for (int[] record : transactions) {
                        if (record[i] == 1) countA++;
                        if (record[j] == 1) countB++;
                        if (record[i] == 1 && record[j] == 1) countAB++;
                    }

                    double support = (double) countAB / n;
                    double confidence = countA == 0 ? 0 : (double) countAB / countA;
                    double lift = (countB == 0) ? 0 : confidence / ((double) countB / n);

                    Map<String, Object> rule = new LinkedHashMap<>();
                    rule.put("规则", itemNames.get(i) + " → " + itemNames.get(j));
                    rule.put("支持度", String.format("%.4f", support));
                    rule.put("置信度", String.format("%.4f", confidence));
                    rule.put("提升度", String.format("%.4f", lift));
                    rule.put("关系", lift > 1 ? "正相关" : (lift < 1 ? "负相关" : "无关联"));

                    rules.add(rule);
                }
            }

            result.put("样本总数", n);
            result.put("项目数", itemNames.size());
            result.put("关联规则结果", rules);
            result.put("status", "success");

        } catch (Exception e) {
            result.put("status", "error");
            result.put("message", "计算失败：" + e.getMessage());
        }

        return result;
    }
}



